package skewreduce.framework;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Arrays;

import org.apache.hadoop.io.Writable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import skewreduce.lib.ITuple;


// FIXME so this must be Y, X not X, Y. should do some clarification or
//   dynamically order it
public class ArrayIndex implements Writable, Cloneable, ITuple {
    private static final Logger LOG = LoggerFactory.getLogger(ArrayIndex.class);

    private boolean invalid;
    private final ArrayDimension dim;
    private final int[] indexes;
    
    ArrayIndex(ArrayDimension d) {
        dim = d;
        indexes = new int[dim.size()];
    }
    
    ArrayIndex(ArrayDimension d,int... idx) {
        dim = d;
        indexes = idx;
    }
    
    public ArrayDimension getDimension() { return dim; }
    public int get(int dim) { return indexes[dim]; }
    public int getDimensionLength() { return indexes.length; }
    public int size() { return indexes.length << 2; }

    @Override
    public void readFields(DataInput in) throws IOException {
        for ( int i = 0; i < indexes.length; ++i ) {
            indexes[i] = in.readInt();
        }
    }

    @Override
    public void write(DataOutput out) throws IOException {
        for ( int i : indexes ) {
            out.writeInt(i);
        }
    }
    
    @Override
    public String toString() {
        return Arrays.toString(indexes);
    }
    
    public ArrayIndex clone() {
        try {
            return (ArrayIndex)super.clone();
        } catch (CloneNotSupportedException e) {
            throw new IllegalStateException(e);
        }
    }
    
    public ArrayIndex incr() {
        if ( invalid ) {
            throw new IllegalStateException("Index overflow");
        }

        ArrayIndex ret = clone();

        // X - Y order
        int d = 0;
        while ( d < indexes.length ) {
            ++indexes[d];
            if ( indexes[d] < dim.getMax(d) ) break;
            indexes[d] = 0;
            ++d;
        }
        
        invalid = d == indexes.length;
 
        /* 
        // Y - X order
        int d = indexes.length - 1;
        while ( d >= 0 ) {
            ++indexes[d];
            if ( indexes[d] < dim.getMax(d) ) break;
            indexes[d] = 0;
            --d;
        }
        
        if ( d < 0 )
            throw new IllegalStateException();
        */
        return ret;
    }
    
    public ArrayIndex incr(int i) {
        if ( invalid ) {
            throw new IllegalStateException("Index overflow");
        }

        ArrayIndex ret = clone();

        // X - Y order
        int d = 0;
        int inc = i;
        while ( d < indexes.length ) {
            indexes[d] += inc;
            if ( indexes[d] < dim.getMax(d) ) break;
            inc = indexes[d] / dim.getMax(d);
            indexes[d] %= dim.getMax(d);
            ++d;
        }
        
        invalid = d == indexes.length;
 
        /* 
        // Y - X order
        int d = indexes.length - 1;
        int inc = i;
        while ( d >= 0 ) {
            indexes[d] += inc;
            if ( indexes[d] < dim.getMax(d) ) break;
            inc = indexes[d] / dim.getMax(d);
            indexes[d] %= dim.getMax(d);
            --d;
        }
        
        if ( d < 0 && inc > 0 )
            throw new IllegalStateException();
        */
        return ret;
    }

    @Override
    public int arity() { return indexes.length; }
    @Override
    public Object getValue(int i) { return indexes[i]; }
    @Override
    public Type getType(int i) { return Type.INTEGER; }

    @Override
	public float getFloat(int i) {
        throw new UnsupportedOperationException();
    }
    @Override
	public double getDouble(int i) {
        throw new UnsupportedOperationException();
    }
    @Override
	public int getInt(int i) { return indexes[i]; }
    @Override
	public long getLong(int i) { return indexes[i]; }
    @Override
	public String getString(int i) {
        throw new UnsupportedOperationException();
    }

	@Override
	public boolean equals(ITuple o) {
		if ( o instanceof ArrayIndex ) {
			ArrayIndex b = (ArrayIndex)o;
			if ( ! dim.equals(b.dim) ) return false;
			return Arrays.equals(indexes, b.indexes);
		} else {
			if ( o.arity() != indexes.length ) return false;
			for ( int i = 0; i < indexes.length; ++i ) {
				if ( indexes[i] != o.getInt(i) ) return false;
			}
		}
		return true;
	}
}
